/**
 * Copyright (c) 2023 murenchao
 * taomu is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.toolkit

import com.google.common.collect.ArrayListMultimap
import com.google.inject.Binder
import com.google.inject.name.Names
import cool.taomu.toolkit.crypto.ICrypto
import cool.taomu.toolkit.entity.TaomuConfig
import java.io.InputStream
import java.util.ArrayList
import java.util.LinkedHashMap
import java.util.List
import java.util.Map
import java.util.Properties
import java.util.regex.Pattern
import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
import org.yaml.snakeyaml.Yaml

class YamlUtils {
    val static LOG = LoggerFactory.getLogger(YamlUtils);

    ICrypto enc;

    new(ICrypto enc) {
        this.enc = enc
    }
    
     def toPropertiesMap(InputStream filename) {
        val m = ArrayListMultimap.create();
        var yml = new Yaml()
        var results = yml.loadAs(filename, Map)
        results = decryptEncryptedValues(results) as Map
        results = convertKeysToCamelCase(results) as Map
        results.forEach [ k, v |
            if (v instanceof Map) {
                toProperty(m, k, v)
            } else {
                m.put(k, v);
            }
        ]
        return m.asMap
    }

    def Properties toProperties(InputStream filename, Binder binder) {
        val m = ArrayListMultimap.create();
        var yml = new Yaml()
        var results = yml.loadAs(filename, Map)
        results = decryptEncryptedValues(results) as Map;
        results.forEach [ k, v |
            if (v instanceof Map) {
                toProperty(m, k, v)
            } else {
                m.put(k, v);
            }
        ]
        val pro = new Properties();
        m.asMap.forEach [ k, v |
            pro.put(k, v.get(0))
            var ^val = v.get(0);
            LOG.info(k + "=> value type =>" + ^val.class.name)
            if (binder !== null) {
                binder.bind(k, ^val)
            }
        ]
        return pro;
    }

    def dispatch Object decryptEncryptedValues(Object obj) {
        return obj;
    }

    def dispatch Object decryptEncryptedValues(Map obj) {
        obj.forEach([ k, v |
            obj.put(k, decryptEncryptedValues(v))
        ]);
        return obj;
    }

    def dispatch Object decryptEncryptedValues(List obj) {
        for (var int i = 0; i < obj.size(); i++) {
            obj.set(i, decryptEncryptedValues(obj.get(i)));
        }
        return obj;
    }

    def dispatch Object decryptEncryptedValues(String obj) {
        var m = Pattern.compile("^ENC\\((.*?)\\)$").matcher(obj);
        if (m.find()) {
            LOG.info("开始解码ENC");
            return decrypt(m.group(1)); // 自定义解密方法
        }
        return obj;
    }

    def decrypt(String strings) {
        if (this.enc !== null) {
            this.enc.data = strings.bytes
            return new String(this.enc.decode, "UTF-8");
        }
        return strings
    }

    def dispatch bind(Binder binder, String k, String ^val) {
        LOG.info("bind String");
        binder.bindConstant().annotatedWith(Names.named(k)).to(^val)
    }

    def dispatch bind(Binder binder, String k, Integer ^val) {
        LOG.info("bind Integer");
        binder.bindConstant().annotatedWith(Names.named(k)).to(^val)
    }

    def dispatch bind(Binder binder, String k, Boolean ^val) {
        LOG.info("bind boolean");
        binder.bindConstant().annotatedWith(Names.named(k)).to(^val)
    }

    def dispatch bind(Binder binder, String k, Object ^val) {
        LOG.info("bind {} => {}", k, ^val.class.name)
        binder.bind(TaomuConfig).annotatedWith(Names.named(k)).toInstance(new TaomuConfig(^val))
    }

    def void toProperty(ArrayListMultimap<String, Object> convert, String key, Map<String, Object> map) {
        map.forEach [ k, v |
            val String namespace = StringUtils.isNotBlank(key) ? #[key, k].join(".") : k
            if (v instanceof Map) {
                toProperty(convert, namespace, v);
            } else {
                convert.put(namespace, v);
            }
        ]
    }
    
     // 新增：将 kebab-case 转换为 camelCase
    def static String toCamelCase(String input) {
        val sb = new StringBuilder()
        var nextUpper = false

        for (i : 0..input.length - 1) {
            val c = input.charAt(i)
            //System.out.println("Character at " + i + ": '" + c + "' (" + c as int + ")")
            if (c as int == 45) {
                nextUpper = true
            } else {
                if (nextUpper && sb.length > 0) {
                    sb.append(Character.toUpperCase(c))
                    nextUpper = false
                } else {
                    sb.append(c)
                }
            }
        }

        return sb.toString()
    }

    // 新增：递归地将 Map 的 key 转换为 camelCase
    def private Object convertKeysToCamelCase(Object obj) {
        if (obj instanceof Map) {
            val map = obj as Map<String, Object>
            val result = new LinkedHashMap<String, Object>()
            map.forEach [ k, v |
                val newKey = toCamelCase(k)
                result.put(newKey, convertKeysToCamelCase(v))
            ]
            return result
        } else if (obj instanceof List) {
            val list = obj as List<Object>
            val result = new ArrayList<Object>()
            for (item : list) {
                result.add(convertKeysToCamelCase(item))
            }
            return result
        } else {
            return obj
        }
    }
}
